fuente: https://github.com/DiegoKoz/MIA_text_mining

Warning: Este dataset es muy pesado. Eso implica que lleva tiempo correr los modelos y que puede no entrar en la memoria de la computadora. Para la clase, se puede hacer un muestreo de textos para que no pese tanto

library(tidyverse)
── Attaching packages ───────────────────────────────────────────────────────────────────────────────────────────────────────── tidyverse 1.2.1 ──
✔ ggplot2 3.2.0     ✔ purrr   0.3.2
✔ tibble  2.1.3     ✔ dplyr   0.8.3
✔ tidyr   0.8.3     ✔ stringr 1.4.0
✔ readr   1.3.1     ✔ forcats 0.4.0
── Conflicts ──────────────────────────────────────────────────────────────────────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter()  masks stats::filter()
✖ purrr::is_null() masks testthat::is_null()
✖ dplyr::lag()     masks stats::lag()
✖ dplyr::matches() masks testthat::matches()
library(glue)

Attaching package: ‘glue’

The following object is masked from ‘package:dplyr’:

    collapse
library(tm)
Loading required package: NLP

Attaching package: ‘NLP’

The following object is masked from ‘package:ggplot2’:

    annotate
library(topicmodels)
library(tidytext)
library(stringi)
library(LDAvis)
library(slam)
library(tsne)
library(lubridate)

Attaching package: ‘lubridate’

The following object is masked from ‘package:base’:

    date
library(DT)
library(lsa)
Loading required package: SnowballC
library(igraph)

Attaching package: ‘igraph’

The following objects are masked from ‘package:lubridate’:

    %--%, union

The following objects are masked from ‘package:dplyr’:

    as_data_frame, groups, union

The following objects are masked from ‘package:purrr’:

    compose, simplify

The following object is masked from ‘package:tidyr’:

    crossing

The following object is masked from ‘package:tibble’:

    as_data_frame

The following objects are masked from ‘package:stats’:

    decompose, spectrum

The following object is masked from ‘package:testthat’:

    compare

The following object is masked from ‘package:base’:

    union
library(ggraph)
library(tidygraph)

Attaching package: ‘tidygraph’

The following object is masked from ‘package:igraph’:

    groups

The following object is masked from ‘package:stats’:

    filter

The following object is masked from ‘package:testthat’:

    matches

df <- read_csv('../data/txt/texto_limpio.txt')
Parsed with column specification:
cols(
  tipo = col_character(),
  autor = col_character(),
  titulo = col_character(),
  texto = col_character(),
  link = col_character(),
  link1 = col_character(),
  fecha = col_double(),
  id = col_character()
)
# df <- read_rds('data/MIA.RDS')
df <- df %>% 
  filter(tipo=='notas')
df <- df %>% 
  mutate(texto = tolower(texto),
         texto = stri_trans_general(texto, "Latin-ASCII"),
         texto = str_trim(texto,side = 'both'),
         texto = str_replace_all(texto,'\t',' '),
         texto = str_replace_all(texto,'\n',' '),
         texto = str_replace_all(texto,'\r',' '),
         texto = str_replace_all(texto,'[[:punct:]]',' '),
         texto = str_remove_all(texto,'\\d'),
         # texto = str_replace_all(texto,'\\d','NUM'),
         # texto = str_replace_all(texto,'(NUM)+','NUM'),
         texto = str_replace_all(texto,"\\s+", " "))

Para topic modeling las palabras comunes de la lengua generan mucho ruido y terminan predominnado en los topicos.

Vamos a eliminar no solo las Stop Words, sino también las palabras más utilizadas en el español que no están relacionadas con nuestra temática. Para eso, tenemos un archivo r_words.txt donde pusimos todas las palabras más comunes.

Además, aprovechamos para eliminar los tokens que quedaron del scrapeo que en realidad son parte del código html (ver final del archivo).

¿de donde salieron estos tokens? En una primera iteración del LDA, uno de los tópicos que se armó era de código html.

palabras_comunes <- read_csv(file = 'data/r_words.txt',col_names = F)
Parsed with column specification:
cols(
  X1 = col_character()
)
palabras_comunes <-stri_trans_general(palabras_comunes$X1, "Latin-ASCII") # le tengo que hacer la misma transformacion que al texto
stop_words <- stri_trans_general(stopwords(kind = "es"), "Latin-ASCII")

palabras_eliminar <- unique(c(stop_words,palabras_comunes))

rm(stop_words)
rm(palabras_comunes)
gc()
           used  (Mb) gc trigger  (Mb) max used  (Mb)
Ncells  2213246 118.3    4018710 214.7  4018710 214.7
Vcells 12067907  92.1   40243535 307.1 33467399 255.4

Corpus = VCorpus(VectorSource(df$texto))
Corpus = tm_map(Corpus, removeWords, palabras_eliminar)
# Corpus <- tm_map(Corpus, stemDocument, language = "spanish") # Corpus  

dtm <- DocumentTermMatrix(Corpus)
rm(Corpus)
gc()
           used  (Mb) gc trigger  (Mb) max used  (Mb)
Ncells  2348239 125.5    4018710 214.7  4018710 214.7
Vcells 16995397 129.7   40243535 307.1 39272344 299.7
# tm::nTerms(dtm)
#elimino los docuemntos vacios
# rowTotals <- rowSums(as.matrix(dtm))
# nDocs(dtm)
# dtm   <- dtm[rowTotals> 0, ]
# nDocs(dtm)

write_rds(dtm, 'data/dtm_MIA.rds')

# df <- df[which(rowTotals>0),] #%>%  #tengo que eliminar ese docuemnto que estaba vacio
dtm <- read_rds('data/dtm_MIA.rds')

limpio la memoria porque ya no me queda espacio

rm(palabras_eliminar)
gc()
           used  (Mb) gc trigger  (Mb) max used  (Mb)
Ncells  2348089 125.5    4018710 214.7  4018710 214.7
Vcells 16995237 129.7   40243535 307.1 39272344 299.7
lda_fit <- LDA(dtm, k = 20,method = "Gibbs", control = list(delta=0.6,seed = 1234))
lda_fit
A LDA_Gibbs topic model with 20 topics.
saveRDS(lda_fit, 'modelos/MIA_lda20.rds') # Tarda mucho en correr, asi que guardamos los resultados
lda_fit <- read_rds('modelos/MIA_lda20.rds')
Terms <- terms(lda_fit, 10)
Terms
      Topic 1       Topic 2     Topic 3       Topic 4    Topic 5        Topic 6      Topic 7         Topic 8     Topic 9        Topic 10    
 [1,] "tratamiento" "lenin"     "mitin"       "hombre"   "gobierno"     "gramsci"    "escritos"      "stalin"    "anapo"        "convocado" 
 [2,] "aplicables"  "carta"     "satelite"    "ser"      "pueblo"       "cuadernos"  "oposicion"     "trotsky"   "colombiana"   "judiciales"
 [3,] "link"        "tomo"      "descargar"   "hombres"  "chile"        "ordine"     "internacional" "moscu"     "lleras"       "meses"     
 [4,] "nestor"      "libro"     "formato"     "sociedad" "pais"         "nuovo"      "trotsky"       "gpu"       "mrl"          "und"       
 [5,] "paradojas"   "editorial" "link"        "vida"     "trabajadores" "antonio"    "partido"       "comite"    "caudillismo"  "vino"      
 [6,] "significado" "revista"   "parisino"    "mujeres"  "popular"      "ever"       "stalin"        "oposicion" "anapistas"    "absuelto"  
 [7,] "acosado"     "cartas"    "reconquista" "sino"     "presidente"   "aga"        "izquierda"     "central"   "anapista"     "aclamada"  
 [8,] "agradaria"   "ulianova"  "abandonar"   "mujer"    "companeros"   "distorsion" "carta"         "lenin"     "valencia"     "actualidad"
 [9,] "aprension"   "obras"     "acepta"      "social"   "nacional"     "pci"        "sovietica"     "anos"      "bipartidismo" "afirmais"  
[10,] "blind"       "primera"   "aplicara"    "mundo"    "ser"          "carcel"     "comintern"     "juicio"    "pinilla"      "antiguedad"
      Topic 11       Topic 12     Topic 13          Topic 14       Topic 15      Topic 16    Topic 17     Topic 18        Topic 19       
 [1,] "partido"      "bolivia"    "islamistas"      "eisermann"    "trabajo"     "espana"    "china"      "lula"          "indice"       
 [2,] "revolucion"   "casa"       "islam"           "enviadas"     "produccion"  "espanola"  "mao"        "formulaba"     "organo"       
 [3,] "lucha"        "mariategui" "islamismo"       "humor"        "economia"    "poum"      "chino"      "introducido"   "pitt"         
 [4,] "politica"     "carlos"     "musulmanes"      "pertenencias" "capitalista" "cataluna"  "tse"        "plato"         "reuniones"    
 [5,] "clase"        "hombres"    "jomeini"         "pottier"      "industria"   "barcelona" "tung"       "abandonaran"   "aplastando"   
 [6,] "guerra"       "lora"       "mezquitas"       "reciba"       "capital"     "espanol"   "albania"    "agencias"      "conservado"   
 [7,] "proletariado" "noche"      "arabia"          "rompiendo"    "sistema"     "madrid"    "rda"        "alejados"      "dada"         
 [8,] "masas"        "villa"      "fundamentalismo" "www"          "campesinos"  "mfa"       "chinos"     "american"      "dobles"       
 [9,] "poder"        "oro"        "fis"             "abarca"       "propiedad"   "nin"       "kuomintang" "aparentemente" "especializado"
[10,] "obreros"      "nota"       "islamica"        "aisladas"     "economica"   "cnt"       "tsetung"    "apoyaremos"    "gordos"       
      Topic 20    
 [1,] "ejercito"  
 [2,] "frente"    
 [3,] "rojo"      
 [4,] "militares" 
 [5,] "guerra"    
 [6,] "militar"   
 [7,] "sovietico" 
 [8,] "rusia"     
 [9,] "campesinos"
[10,] "pueblo"    
diccionario <- tibble(
n_topico = 1:20,
nombre_topico = c('perestroika','formato','interna_bolche','pcf','expulsados','orwell','?','althusser','turcos','bolivia','longuet','ejercito','chile','españa','indoamericana','lenin','argentina','china','trabajo_prod','partido')
)

Visualizacion

topicmodels_json_ldavis <- function(fitted, dtm){
    svd_tsne <- function(x) tsne(svd(x)$u)

    # Find required quantities
    phi <- as.matrix(posterior(fitted)$terms)
    theta <- as.matrix(posterior(fitted)$topics)
    vocab <- colnames(phi)
    term_freq <- slam::col_sums(dtm)

    # Convert to json
    json_lda <- LDAvis::createJSON(phi = phi, theta = theta,
                            vocab = vocab,
                            mds.method = svd_tsne,
                            plot.opts = list(xlab="tsne", ylab=""),
                            doc.length = as.vector(table(dtm$i)),
                            term.frequency = term_freq)

    return(json_lda)
}
json_res <- topicmodels_json_ldavis(lda_fit, dtm)
sigma summary: Min. : 33554432 |1st Qu. : 33554432 |Median : 33554432 |Mean : 33554432 |3rd Qu. : 33554432 |Max. : 33554432 |
Epoch: Iteration #100 error is: 9.2235707542836
Epoch: Iteration #200 error is: 0.575966219423543
Epoch: Iteration #300 error is: 0.425676483504934
Epoch: Iteration #400 error is: 0.415121366960652
Epoch: Iteration #500 error is: 0.412188213579679
Epoch: Iteration #600 error is: 0.411756026593054
Epoch: Iteration #700 error is: 0.411342066613491
Epoch: Iteration #800 error is: 0.410683708963011
Epoch: Iteration #900 error is: 0.409861178035016
Epoch: Iteration #1000 error is: 0.409529777397322
serVis(json_res,as.gist = T,open.browser = T)
Loading required namespace: gistr

Se puede calcular el tema del que habla en promedio cada autor.

topicos_tsne <- tsne(dist_topicos[3:22],k = 2)
sigma summary: Min. : 0.425038112521257 |1st Qu. : 0.562747476200782 |Median : 0.637153108892315 |Mean : 0.668023052516373 |3rd Qu. : 0.745184867412922 |Max. : 1.34741741045709 |
Epoch: Iteration #100 error is: 15.493094158414
Epoch: Iteration #200 error is: 0.609918815143583
Epoch: Iteration #300 error is: 0.594253465624274
Epoch: Iteration #400 error is: 0.558116694765364
Epoch: Iteration #500 error is: 0.556709057687742
Epoch: Iteration #600 error is: 0.556395175671243
Epoch: Iteration #700 error is: 0.556288870636768
Epoch: Iteration #800 error is: 0.556219466447248
Epoch: Iteration #900 error is: 0.556214841354826
Epoch: Iteration #1000 error is: 0.556214681009996

topicos_tsne <- as_tibble(topicos_tsne,.name_repair = ~glue('tsne_proj_{c(1,2)}'))

dist_topicos %>% bind_cols(topicos_tsne) %>% 
  ggplot(aes(tsne_proj_1,tsne_proj_2, label=autor, color=fecha)) + 
  geom_text()+
  theme_minimal()+
  scale_color_gradient2(low = 'darkorange3',mid ='darkgreen' , high = 'dodgerblue', midpoint = 1850)


dist_topicos %>% bind_cols(topicos_tsne) %>% 
  filter(!is.na(fecha), fecha>1800) %>% 
  ggplot(aes(tsne_proj_1,tsne_proj_2, label=autor, color=fecha)) + 
  geom_text()+
  theme_minimal()+
  scale_color_gradient2(low = 'darkorange3',mid ='darkgreen' , high = 'dodgerblue', midpoint = 1940)

topicos_pca <- princomp(dist_topicos[3:22], cor = TRUE)

dist_topicos %>% bind_cols(as_tibble(topicos_pca$scores[,1:2])) %>% 
  ggplot(aes(Comp.1,Comp.2, label=autor, color = fecha)) + 
  geom_text()+
  theme_minimal()+
  scale_color_gradient2(low = 'darkorange3',mid ='darkgreen' , high = 'dodgerblue', midpoint = 1850)

NA
NA
dist_topicos %>% bind_cols(as_tibble(topicos_pca$scores[,1:2])) %>% 
  filter(!is.na(fecha),fecha>1800) %>% 
  ggplot(aes(Comp.1,Comp.2, label=autor, color = fecha)) + 
  geom_text()+
  theme_minimal()+
  scale_color_gradient2(low = 'darkorange3',mid ='darkgreen' , high = 'dodgerblue', midpoint = 1940)

Por fecha

dist_topicos <- df  %>% 
  select(autor, fecha) %>% 
  bind_cols(as_tibble(as.matrix(posterior(lda_fit)$topics)))


# names(dist_topicos)[3:22] <- diccionario$nombre_topico
plot <- dist_topicos %>% 
  select(-autor) %>%
  mutate(fecha= round(fecha, -1)) %>% 
  group_by(fecha) %>% 
  summarise_all(~mean(.x, na.rm = T)) %>% 
  filter(!is.na(fecha), fecha>1800) %>% 
  gather(topico, valor,2:21) %>% 
  mutate(topico=factor(topico)) %>% 
  ggplot(aes(fecha, valor, group=topico, color=topico, fill=topico))+
  geom_line()+
  scale_x_continuous(breaks = scales::pretty_breaks(10))+
  # directlabels::geom_dl(aes(label = topico), method=list("top.qp", cex = .75))+
  theme_minimal()
  theme(legend.position = 'none')
List of 1
 $ legend.position: chr "none"
 - attr(*, "class")= chr [1:2] "theme" "gg"
 - attr(*, "complete")= logi FALSE
 - attr(*, "validate")= logi TRUE
plotly::ggplotly(plot)

obs: El dataset tiene mucho de Allende, Tópico 6. Tal vez habria que subsamplear.

Terms
      Topic 1       Topic 2     Topic 3       Topic 4    Topic 5        Topic 6      Topic 7         Topic 8     Topic 9        Topic 10    
 [1,] "tratamiento" "lenin"     "mitin"       "hombre"   "gobierno"     "gramsci"    "escritos"      "stalin"    "anapo"        "convocado" 
 [2,] "aplicables"  "carta"     "satelite"    "ser"      "pueblo"       "cuadernos"  "oposicion"     "trotsky"   "colombiana"   "judiciales"
 [3,] "link"        "tomo"      "descargar"   "hombres"  "chile"        "ordine"     "internacional" "moscu"     "lleras"       "meses"     
 [4,] "nestor"      "libro"     "formato"     "sociedad" "pais"         "nuovo"      "trotsky"       "gpu"       "mrl"          "und"       
 [5,] "paradojas"   "editorial" "link"        "vida"     "trabajadores" "antonio"    "partido"       "comite"    "caudillismo"  "vino"      
 [6,] "significado" "revista"   "parisino"    "mujeres"  "popular"      "ever"       "stalin"        "oposicion" "anapistas"    "absuelto"  
 [7,] "acosado"     "cartas"    "reconquista" "sino"     "presidente"   "aga"        "izquierda"     "central"   "anapista"     "aclamada"  
 [8,] "agradaria"   "ulianova"  "abandonar"   "mujer"    "companeros"   "distorsion" "carta"         "lenin"     "valencia"     "actualidad"
 [9,] "aprension"   "obras"     "acepta"      "social"   "nacional"     "pci"        "sovietica"     "anos"      "bipartidismo" "afirmais"  
[10,] "blind"       "primera"   "aplicara"    "mundo"    "ser"          "carcel"     "comintern"     "juicio"    "pinilla"      "antiguedad"
      Topic 11       Topic 12     Topic 13          Topic 14       Topic 15      Topic 16    Topic 17     Topic 18        Topic 19       
 [1,] "partido"      "bolivia"    "islamistas"      "eisermann"    "trabajo"     "espana"    "china"      "lula"          "indice"       
 [2,] "revolucion"   "casa"       "islam"           "enviadas"     "produccion"  "espanola"  "mao"        "formulaba"     "organo"       
 [3,] "lucha"        "mariategui" "islamismo"       "humor"        "economia"    "poum"      "chino"      "introducido"   "pitt"         
 [4,] "politica"     "carlos"     "musulmanes"      "pertenencias" "capitalista" "cataluna"  "tse"        "plato"         "reuniones"    
 [5,] "clase"        "hombres"    "jomeini"         "pottier"      "industria"   "barcelona" "tung"       "abandonaran"   "aplastando"   
 [6,] "guerra"       "lora"       "mezquitas"       "reciba"       "capital"     "espanol"   "albania"    "agencias"      "conservado"   
 [7,] "proletariado" "noche"      "arabia"          "rompiendo"    "sistema"     "madrid"    "rda"        "alejados"      "dada"         
 [8,] "masas"        "villa"      "fundamentalismo" "www"          "campesinos"  "mfa"       "chinos"     "american"      "dobles"       
 [9,] "poder"        "oro"        "fis"             "abarca"       "propiedad"   "nin"       "kuomintang" "aparentemente" "especializado"
[10,] "obreros"      "nota"       "islamica"        "aisladas"     "economica"   "cnt"       "tsetung"    "apoyaremos"    "gordos"       
      Topic 20    
 [1,] "ejercito"  
 [2,] "frente"    
 [3,] "rojo"      
 [4,] "militares" 
 [5,] "guerra"    
 [6,] "militar"   
 [7,] "sovietico" 
 [8,] "rusia"     
 [9,] "campesinos"
[10,] "pueblo"    

grafo de autores

dist_topicos_autor <- dist_topicos %>% 
  group_by(autor) %>% 
  summarise_all(~mean(.x, na.rm = T)) %>% 
  mutate(fecha=round(fecha))

adjMat = cosine(t(as.matrix(dist_topicos_autor[,3:21]))) 

colnames(adjMat) <- dist_topicos_autor$autor
rownames(adjMat) <- dist_topicos_autor$autor

adjMat[1:5,1:5]
                       Albert Einstein Albert Mathiez Albert Rhys Williams Alberto Flores Galindo Alejandra Kollontai
Albert Einstein              1.0000000      0.5177958            0.3898993              0.3792780           0.9242230
Albert Mathiez               0.5177958      1.0000000            0.8931141              0.8766538           0.7893882
Albert Rhys Williams         0.3898993      0.8931141            1.0000000              0.9291002           0.6906881
Alberto Flores Galindo       0.3792780      0.8766538            0.9291002              1.0000000           0.6673232
Alejandra Kollontai          0.9242230      0.7893882            0.6906881              0.6673232           1.0000000
fivenum(adjMat)
[1] 0.0390587 0.4391939 0.7288184 0.9135718 1.0000000
#la paso a dicotomica, no quiero que me quede muy densa, asi que pongo como punto de corte un valor alto

adjMat[adjMat>0.9] <- 1

adjMat[adjMat<0.9] <- 0

adjMat[1:5,1:5]
                       Albert Einstein Albert Mathiez Albert Rhys Williams Alberto Flores Galindo Alejandra Kollontai
Albert Einstein                      1              0                    0                      0                   1
Albert Mathiez                       0              1                    0                      0                   0
Albert Rhys Williams                 0              0                    1                      1                   0
Alberto Flores Galindo               0              0                    1                      1                   0
Alejandra Kollontai                  1              0                    0                      0                   1
g = graph_from_adjacency_matrix(adjMat, weighted= NULL, mode="undirected", diag=FALSE)

V(g)$fecha <- dist_topicos_autor$fecha #agrego la fecha como atributo de cada autor 
mean(degree(g))
[1] 57.93365


l <- layout_nicely(g)

plot(g,edge.arrow.size=.2, vertex.size=4,vertex.frame.color="#ffffff",
             vertex.label="", vertex.label.color="black", 
             layout=l)

as_tbl_graph(g) %>% 
  filter(fecha>1800) %>% 
  activate(nodes) %>%
  mutate(importance = centrality_degree()) %>% 
  filter(importance >1) %>% 
# as_tbl_graph(g) %>% 
#   filter(fecha>1800,
#          !degree(g)<2) %>% 
ggraph() + 
    geom_edge_link(color='grey') + 
    geom_node_point(aes(color=fecha))+
    geom_node_text(aes(label=name),check_overlap = T,nudge_y =-.5 ) +
  theme_void()+
  scale_color_gradient2(low = 'darkorange3',mid ='darkgreen' , high = 'dodgerblue', midpoint = 1900)
Using `nicely` as default layout

as_tbl_graph(g) %>% 
  filter(fecha>1800) %>% 
  activate(nodes) %>%
# as_tbl_graph(g) %>% 
#   filter(fecha>1800,
#          !degree(g)<2) %>% 
ggraph() + 
    geom_edge_link(color='grey') + 
    geom_node_point(aes(color=fecha))+
    geom_node_text(aes(label=name),check_overlap = T,nudge_y =-.5 ) +
  theme_void()+
  scale_color_gradient2(low = 'darkorange3',mid ='darkgreen' , high = 'dodgerblue', midpoint = 1900)
Using `nicely` as default layout

grafo_plot <- as_tbl_graph(g) %>% 
  filter(fecha>1800) %>% 
  activate(nodes) %>%
# as_tbl_graph(g) %>% 
#   filter(fecha>1800,
#          !degree(g)<2) %>% 
ggraph() + 
    geom_edge_link(color='grey') + 
    geom_node_point(aes(color=fecha,label=name))+
    geom_node_text(aes(label=name),check_overlap = T,nudge_y =-.5 ) +
  theme_void()+
  scale_color_gradient2(low = 'darkorange3',mid ='darkgreen' , high = 'dodgerblue', midpoint = 1900)
Using `nicely` as default layout
Ignoring unknown aesthetics: label
plotly::ggplotly(grafo_plot)
geom_GeomEdgePath() has yet to be implemented in plotly.
  If you'd like to see this geom implemented,
  Please open an issue with your example code at
  https://github.com/ropensci/plotly/issues
LS0tCnRpdGxlOiAiTWFyeGlzdHMgSW50ZXJuZXQgQXJjaGl2ZSBMREEiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCmZ1ZW50ZTogaHR0cHM6Ly9naXRodWIuY29tL0RpZWdvS296L01JQV90ZXh0X21pbmluZwoKPiBXYXJuaW5nOiBFc3RlIGRhdGFzZXQgZXMgbXV5IHBlc2Fkby4gRXNvIGltcGxpY2EgcXVlIGxsZXZhIHRpZW1wbyBjb3JyZXIgbG9zIG1vZGVsb3MgeSBxdWUgcHVlZGUgbm8gZW50cmFyIGVuIGxhIG1lbW9yaWEgZGUgbGEgY29tcHV0YWRvcmEuIFBhcmEgbGEgY2xhc2UsIHNlIHB1ZWRlIGhhY2VyIHVuIG11ZXN0cmVvIGRlIHRleHRvcyBwYXJhIHF1ZSBubyBwZXNlIHRhbnRvCgpgYGB7ciBzZXR1cH0KbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoZ2x1ZSkKbGlicmFyeSh0bSkKbGlicmFyeSh0b3BpY21vZGVscykKbGlicmFyeSh0aWR5dGV4dCkKbGlicmFyeShzdHJpbmdpKQpsaWJyYXJ5KExEQXZpcykKbGlicmFyeShzbGFtKQpsaWJyYXJ5KHRzbmUpCmxpYnJhcnkobHVicmlkYXRlKQpsaWJyYXJ5KERUKQpsaWJyYXJ5KGxzYSkKbGlicmFyeShpZ3JhcGgpCmxpYnJhcnkoZ2dyYXBoKQpsaWJyYXJ5KHRpZHlncmFwaCkKYGBgCgpgYGB7cn0KCmRmIDwtIHJlYWRfY3N2KCcuLi9kYXRhL3R4dC90ZXh0b19saW1waW8udHh0JykKCiMgZGYgPC0gcmVhZF9yZHMoJ2RhdGEvTUlBLlJEUycpCmRmIDwtIGRmICU+JSAKICBmaWx0ZXIodGlwbz09J25vdGFzJykKYGBgCgoKYGBge3J9CmRmIDwtIGRmICU+JSAKICBtdXRhdGUodGV4dG8gPSB0b2xvd2VyKHRleHRvKSwKICAgICAgICAgdGV4dG8gPSBzdHJpX3RyYW5zX2dlbmVyYWwodGV4dG8sICJMYXRpbi1BU0NJSSIpLAogICAgICAgICB0ZXh0byA9IHN0cl90cmltKHRleHRvLHNpZGUgPSAnYm90aCcpLAogICAgICAgICB0ZXh0byA9IHN0cl9yZXBsYWNlX2FsbCh0ZXh0bywnXHQnLCcgJyksCiAgICAgICAgIHRleHRvID0gc3RyX3JlcGxhY2VfYWxsKHRleHRvLCdcbicsJyAnKSwKICAgICAgICAgdGV4dG8gPSBzdHJfcmVwbGFjZV9hbGwodGV4dG8sJ1xyJywnICcpLAogICAgICAgICB0ZXh0byA9IHN0cl9yZXBsYWNlX2FsbCh0ZXh0bywnW1s6cHVuY3Q6XV0nLCcgJyksCiAgICAgICAgIHRleHRvID0gc3RyX3JlbW92ZV9hbGwodGV4dG8sJ1xcZCcpLAogICAgICAgICAjIHRleHRvID0gc3RyX3JlcGxhY2VfYWxsKHRleHRvLCdcXGQnLCdOVU0nKSwKICAgICAgICAgIyB0ZXh0byA9IHN0cl9yZXBsYWNlX2FsbCh0ZXh0bywnKE5VTSkrJywnTlVNJyksCiAgICAgICAgIHRleHRvID0gc3RyX3JlcGxhY2VfYWxsKHRleHRvLCJcXHMrIiwgIiAiKSkKYGBgCgoKClBhcmEgdG9waWMgbW9kZWxpbmcgbGFzIHBhbGFicmFzIGNvbXVuZXMgZGUgbGEgbGVuZ3VhIGdlbmVyYW4gbXVjaG8gcnVpZG8geSB0ZXJtaW5hbiBwcmVkb21pbm5hZG8gZW4gbG9zIHRvcGljb3MuCgpWYW1vcyBhIGVsaW1pbmFyIG5vIHNvbG8gbGFzIFN0b3AgV29yZHMsIHNpbm8gdGFtYmnDqW4gbGFzIHBhbGFicmFzIG3DoXMgdXRpbGl6YWRhcyBlbiBlbCBlc3Bhw7FvbCBxdWUgbm8gZXN0w6FuIHJlbGFjaW9uYWRhcyBjb24gbnVlc3RyYSB0ZW3DoXRpY2EuClBhcmEgZXNvLCB0ZW5lbW9zIHVuIGFyY2hpdm8gcl93b3Jkcy50eHQgZG9uZGUgcHVzaW1vcyB0b2RhcyBsYXMgcGFsYWJyYXMgbcOhcyBjb211bmVzLiAKCkFkZW3DoXMsIGFwcm92ZWNoYW1vcyBwYXJhIGVsaW1pbmFyIGxvcyB0b2tlbnMgcXVlIHF1ZWRhcm9uIGRlbCBzY3JhcGVvIHF1ZSBlbiByZWFsaWRhZCBzb24gcGFydGUgZGVsIGPDs2RpZ28gaHRtbCAodmVyIGZpbmFsIGRlbCBhcmNoaXZvKS4KCsK/ZGUgZG9uZGUgc2FsaWVyb24gZXN0b3MgdG9rZW5zPyBFbiB1bmEgcHJpbWVyYSBpdGVyYWNpw7NuIGRlbCBMREEsIHVubyBkZSBsb3MgdMOzcGljb3MgcXVlIHNlIGFybcOzIGVyYSBkZSBjw7NkaWdvIGh0bWwuIAoKYGBge3J9CnBhbGFicmFzX2NvbXVuZXMgPC0gcmVhZF9jc3YoZmlsZSA9ICdkYXRhL3Jfd29yZHMudHh0Jyxjb2xfbmFtZXMgPSBGKQpwYWxhYnJhc19jb211bmVzIDwtc3RyaV90cmFuc19nZW5lcmFsKHBhbGFicmFzX2NvbXVuZXMkWDEsICJMYXRpbi1BU0NJSSIpICMgbGUgdGVuZ28gcXVlIGhhY2VyIGxhIG1pc21hIHRyYW5zZm9ybWFjaW9uIHF1ZSBhbCB0ZXh0bwpzdG9wX3dvcmRzIDwtIHN0cmlfdHJhbnNfZ2VuZXJhbChzdG9wd29yZHMoa2luZCA9ICJlcyIpLCAiTGF0aW4tQVNDSUkiKQoKcGFsYWJyYXNfZWxpbWluYXIgPC0gdW5pcXVlKGMoc3RvcF93b3JkcyxwYWxhYnJhc19jb211bmVzKSkKCnJtKHN0b3Bfd29yZHMpCnJtKHBhbGFicmFzX2NvbXVuZXMpCmdjKCkKYGBgCgoKYGBge3J9CgpDb3JwdXMgPSBWQ29ycHVzKFZlY3RvclNvdXJjZShkZiR0ZXh0bykpCkNvcnB1cyA9IHRtX21hcChDb3JwdXMsIHJlbW92ZVdvcmRzLCBwYWxhYnJhc19lbGltaW5hcikKIyBDb3JwdXMgPC0gdG1fbWFwKENvcnB1cywgc3RlbURvY3VtZW50LCBsYW5ndWFnZSA9ICJzcGFuaXNoIikgIyBDb3JwdXMgIAoKZHRtIDwtIERvY3VtZW50VGVybU1hdHJpeChDb3JwdXMpCnJtKENvcnB1cykKZ2MoKQojIHRtOjpuVGVybXMoZHRtKQojZWxpbWlubyBsb3MgZG9jdWVtbnRvcyB2YWNpb3MKIyByb3dUb3RhbHMgPC0gcm93U3Vtcyhhcy5tYXRyaXgoZHRtKSkKIyBuRG9jcyhkdG0pCiMgZHRtICAgPC0gZHRtW3Jvd1RvdGFscz4gMCwgXQojIG5Eb2NzKGR0bSkKCndyaXRlX3JkcyhkdG0sICdkYXRhL2R0bV9NSUEucmRzJykKCiMgZGYgPC0gZGZbd2hpY2gocm93VG90YWxzPjApLF0gIyU+JSAgI3RlbmdvIHF1ZSBlbGltaW5hciBlc2UgZG9jdWVtbnRvIHF1ZSBlc3RhYmEgdmFjaW8KYGBgCgoKCmBgYHtyfQpkdG0gPC0gcmVhZF9yZHMoJ2RhdGEvZHRtX01JQS5yZHMnKQpgYGAKCgpsaW1waW8gbGEgbWVtb3JpYSBwb3JxdWUgeWEgbm8gbWUgcXVlZGEgZXNwYWNpbwpgYGB7cn0Kcm0ocGFsYWJyYXNfZWxpbWluYXIpCmdjKCkKYGBgCgoKCmBgYHtyIH0KbGRhX2ZpdCA8LSBMREEoZHRtLCBrID0gMjAsbWV0aG9kID0gIkdpYmJzIiwgY29udHJvbCA9IGxpc3QoZGVsdGE9MC42LHNlZWQgPSAxMjM0KSkKbGRhX2ZpdAoKc2F2ZVJEUyhsZGFfZml0LCAnbW9kZWxvcy9NSUFfbGRhMjAucmRzJykgIyBUYXJkYSBtdWNobyBlbiBjb3JyZXIsIGFzaSBxdWUgZ3VhcmRhbW9zIGxvcyByZXN1bHRhZG9zCmBgYAoKYGBge3J9CmxkYV9maXQgPC0gcmVhZF9yZHMoJ21vZGVsb3MvTUlBX2xkYTIwLnJkcycpCmBgYAoKCmBgYHtyfQpUZXJtcyA8LSB0ZXJtcyhsZGFfZml0LCAxMCkKVGVybXMKYGBgCgoKCgpgYGB7cn0KIyBkaWNjaW9uYXJpbyA8LSB0aWJibGUoCiMgbl90b3BpY28gPSAxOjIwLAojIG5vbWJyZV90b3BpY28gPSBjKCdpbnRlcm5hX2JvbGNoZScsJ2Zvcm1hdG8nLCdpbnRlcm5hX2JvbGNoZScsJ3BjZicsJ2V4cHVsc2Fkb3MnLCdvcndlbGwnLCc/JywnYWx0aHVzc2VyJywndHVyY29zJywnYm9saXZpYScsJ2xvbmd1ZXQnLCdlamVyY2l0bycsJ2NoaWxlJywnZXNwYcOxYScsJ2luZG9hbWVyaWNhbmEnLCdsZW5pbicsJ2FyZ2VudGluYScsJ2NoaW5hJywndHJhYmFqb19wcm9kJywncGFydGlkbycpCiMgKQpgYGAKCgoKVmlzdWFsaXphY2lvbgoKCgoKYGBge3J9CnRvcGljbW9kZWxzX2pzb25fbGRhdmlzIDwtIGZ1bmN0aW9uKGZpdHRlZCwgZHRtKXsKICAgIHN2ZF90c25lIDwtIGZ1bmN0aW9uKHgpIHRzbmUoc3ZkKHgpJHUpCgogICAgIyBGaW5kIHJlcXVpcmVkIHF1YW50aXRpZXMKICAgIHBoaSA8LSBhcy5tYXRyaXgocG9zdGVyaW9yKGZpdHRlZCkkdGVybXMpCiAgICB0aGV0YSA8LSBhcy5tYXRyaXgocG9zdGVyaW9yKGZpdHRlZCkkdG9waWNzKQogICAgdm9jYWIgPC0gY29sbmFtZXMocGhpKQogICAgdGVybV9mcmVxIDwtIHNsYW06OmNvbF9zdW1zKGR0bSkKCiAgICAjIENvbnZlcnQgdG8ganNvbgogICAganNvbl9sZGEgPC0gTERBdmlzOjpjcmVhdGVKU09OKHBoaSA9IHBoaSwgdGhldGEgPSB0aGV0YSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZvY2FiID0gdm9jYWIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBtZHMubWV0aG9kID0gc3ZkX3RzbmUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBwbG90Lm9wdHMgPSBsaXN0KHhsYWI9InRzbmUiLCB5bGFiPSIiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRvYy5sZW5ndGggPSBhcy52ZWN0b3IodGFibGUoZHRtJGkpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRlcm0uZnJlcXVlbmN5ID0gdGVybV9mcmVxKQoKICAgIHJldHVybihqc29uX2xkYSkKfQpgYGAKCmBgYHtyfQpqc29uX3JlcyA8LSB0b3BpY21vZGVsc19qc29uX2xkYXZpcyhsZGFfZml0LCBkdG0pCgpgYGAKCmBgYHtyfQpzZXJWaXMoanNvbl9yZXMsYXMuZ2lzdCA9IFQsb3Blbi5icm93c2VyID0gVCkKYGBgCgojIFNlIHB1ZWRlIGNhbGN1bGFyIGVsIHRlbWEgZGVsIHF1ZSBoYWJsYSBlbiBwcm9tZWRpbyBjYWRhIGF1dG9yLgoKYGBge3J9CgpkaXN0X3RvcGljb3MgPC0gZGYgICU+JSAKICBzZWxlY3QoYXV0b3IsIGZlY2hhKSAlPiUgCiAgYmluZF9jb2xzKGFzX3RpYmJsZShhcy5tYXRyaXgocG9zdGVyaW9yKGxkYV9maXQpJHRvcGljcykpKQoKZGlzdF90b3BpY29zIDwtIGRpc3RfdG9waWNvcyAlPiUgCiAgZ3JvdXBfYnkoYXV0b3IpICU+JSAKICBzdW1tYXJpc2VfYWxsKH5tZWFuKC54LCBuYS5ybSA9IFQpKSAlPiUgCiAgbXV0YXRlKGZlY2hhPXJvdW5kKGZlY2hhKSkKCgojIG5hbWVzKGRpc3RfdG9waWNvcylbMzoyMl0gPC0gZGljY2lvbmFyaW8kbm9tYnJlX3RvcGljbwoKZGlzdF90b3BpY29zICU+JSAKICBmaWx0ZXIoIWlzLm5hKGZlY2hhKSkgJT4lIApkYXRhdGFibGUoLiwgZmlsdGVyID0gJ3RvcCcsZXh0ZW5zaW9ucyA9ICdCdXR0b25zJywgb3B0aW9ucyA9IGxpc3QoZG9tID0gJ0JmcnRpcCcsICBidXR0b25zID0gYygnZXhjZWwnLCAiY3N2IiwgImNvcHkiLCAicGRmIiksICAgcGFnZUxlbmd0aCA9IDIwLCBhdXRvV2lkdGggPSBUUlVFKSxyb3duYW1lcz0gRkFMU0UpICU+JQogICMgZm9ybWF0UGVyY2VudGFnZShkaWNjaW9uYXJpbyRub21icmVfdG9waWNvLCAyKSAlPiUKICAjIGZvcm1hdFN0eWxlKGRpY2Npb25hcmlvJG5vbWJyZV90b3BpY28sIGJhY2tncm91bmQgPSBzdHlsZUNvbG9yQmFyKGMoMCwxKSwgJ2RlZXBza3libHVlJykpICU+JQogICMgZm9ybWF0U3R5bGUoZGljY2lvbmFyaW8kbm9tYnJlX3RvcGljbywgCiAgZm9ybWF0UGVyY2VudGFnZSgzOjIyLCAyKSAlPiUKICBmb3JtYXRTdHlsZSgzOjIyLCBiYWNrZ3JvdW5kID0gc3R5bGVDb2xvckJhcihjKDAsMSksICdkZWVwc2t5Ymx1ZScpKSAlPiUKICBmb3JtYXRTdHlsZSgzOjIyLAogICAgICAgICAgICAgIGJhY2tncm91bmRTaXplID0gJzk4JSA2MCUnLAogICAgICAgICAgICAgIGJhY2tncm91bmRSZXBlYXQgPSAnbm8tcmVwZWF0JywKICAgICAgICAgICAgICBiYWNrZ3JvdW5kUG9zaXRpb24gPSAnY2VudGVyJykKCmBgYAoKYGBge3J9CnRvcGljb3NfdHNuZSA8LSB0c25lKGRpc3RfdG9waWNvc1szOjIyXSxrID0gMikKCmBgYAoKCgpgYGB7ciBmaWcuaGVpZ2h0PTEwLCBmaWcud2lkdGg9MTB9Cgp0b3BpY29zX3RzbmUgPC0gYXNfdGliYmxlKHRvcGljb3NfdHNuZSwubmFtZV9yZXBhaXIgPSB+Z2x1ZSgndHNuZV9wcm9qX3tjKDEsMil9JykpCgpkaXN0X3RvcGljb3MgJT4lIGJpbmRfY29scyh0b3BpY29zX3RzbmUpICU+JSAKICBnZ3Bsb3QoYWVzKHRzbmVfcHJval8xLHRzbmVfcHJval8yLCBsYWJlbD1hdXRvciwgY29sb3I9ZmVjaGEpKSArIAogIGdlb21fdGV4dCgpKwogIHRoZW1lX21pbmltYWwoKSsKICBzY2FsZV9jb2xvcl9ncmFkaWVudDIobG93ID0gJ2RhcmtvcmFuZ2UzJyxtaWQgPSdkYXJrZ3JlZW4nICwgaGlnaCA9ICdkb2RnZXJibHVlJywgbWlkcG9pbnQgPSAxODUwKQoKYGBgCmBgYHtyIGZpZy5oZWlnaHQ9MTAsIGZpZy53aWR0aD0xMH0KCmRpc3RfdG9waWNvcyAlPiUgYmluZF9jb2xzKHRvcGljb3NfdHNuZSkgJT4lIAogIGZpbHRlcighaXMubmEoZmVjaGEpLCBmZWNoYT4xODAwKSAlPiUgCiAgZ2dwbG90KGFlcyh0c25lX3Byb2pfMSx0c25lX3Byb2pfMiwgbGFiZWw9YXV0b3IsIGNvbG9yPWZlY2hhKSkgKyAKICBnZW9tX3RleHQoKSsKICB0aGVtZV9taW5pbWFsKCkrCiAgc2NhbGVfY29sb3JfZ3JhZGllbnQyKGxvdyA9ICdkYXJrb3JhbmdlMycsbWlkID0nZGFya2dyZWVuJyAsIGhpZ2ggPSAnZG9kZ2VyYmx1ZScsIG1pZHBvaW50ID0gMTk0MCkKYGBgCgoKCmBgYHtyICBmaWcuaGVpZ2h0PTEwLCBmaWcud2lkdGg9MTB9CnRvcGljb3NfcGNhIDwtIHByaW5jb21wKGRpc3RfdG9waWNvc1szOjIyXSwgY29yID0gVFJVRSkKCmRpc3RfdG9waWNvcyAlPiUgYmluZF9jb2xzKGFzX3RpYmJsZSh0b3BpY29zX3BjYSRzY29yZXNbLDE6Ml0pKSAlPiUgCiAgZ2dwbG90KGFlcyhDb21wLjEsQ29tcC4yLCBsYWJlbD1hdXRvciwgY29sb3IgPSBmZWNoYSkpICsgCiAgZ2VvbV90ZXh0KCkrCiAgdGhlbWVfbWluaW1hbCgpKwogIHNjYWxlX2NvbG9yX2dyYWRpZW50Mihsb3cgPSAnZGFya29yYW5nZTMnLG1pZCA9J2RhcmtncmVlbicgLCBoaWdoID0gJ2RvZGdlcmJsdWUnLCBtaWRwb2ludCA9IDE4NTApCgoKYGBgCgoKYGBge3IgZmlnLmhlaWdodD0xMCwgZmlnLndpZHRoPTEwfQpkaXN0X3RvcGljb3MgJT4lIGJpbmRfY29scyhhc190aWJibGUodG9waWNvc19wY2Ekc2NvcmVzWywxOjJdKSkgJT4lIAogIGZpbHRlcighaXMubmEoZmVjaGEpLGZlY2hhPjE4MDApICU+JSAKICBnZ3Bsb3QoYWVzKENvbXAuMSxDb21wLjIsIGxhYmVsPWF1dG9yLCBjb2xvciA9IGZlY2hhKSkgKyAKICBnZW9tX3RleHQoKSsKICB0aGVtZV9taW5pbWFsKCkrCiAgc2NhbGVfY29sb3JfZ3JhZGllbnQyKGxvdyA9ICdkYXJrb3JhbmdlMycsbWlkID0nZGFya2dyZWVuJyAsIGhpZ2ggPSAnZG9kZ2VyYmx1ZScsIG1pZHBvaW50ID0gMTk0MCkKCmBgYAoKIyMgUG9yIGZlY2hhCgoKYGBge3J9CmRpc3RfdG9waWNvcyA8LSBkZiAgJT4lIAogIHNlbGVjdChhdXRvciwgZmVjaGEpICU+JSAKICBiaW5kX2NvbHMoYXNfdGliYmxlKGFzLm1hdHJpeChwb3N0ZXJpb3IobGRhX2ZpdCkkdG9waWNzKSkpCgoKIyBuYW1lcyhkaXN0X3RvcGljb3MpWzM6MjJdIDwtIGRpY2Npb25hcmlvJG5vbWJyZV90b3BpY28KCmBgYAoKCmBgYHtyIHdhcm5pbmc9RkFMU0V9CnBsb3QgPC0gZGlzdF90b3BpY29zICU+JSAKICBzZWxlY3QoLWF1dG9yKSAlPiUKICBtdXRhdGUoZmVjaGE9IHJvdW5kKGZlY2hhLCAtMSkpICU+JSAKICBncm91cF9ieShmZWNoYSkgJT4lIAogIHN1bW1hcmlzZV9hbGwofm1lYW4oLngsIG5hLnJtID0gVCkpICU+JSAKICBmaWx0ZXIoIWlzLm5hKGZlY2hhKSwgZmVjaGE+MTgwMCkgJT4lIAogIGdhdGhlcih0b3BpY28sIHZhbG9yLDI6MjEpICU+JSAKICBtdXRhdGUodG9waWNvPWZhY3Rvcih0b3BpY28pKSAlPiUgCiAgZ2dwbG90KGFlcyhmZWNoYSwgdmFsb3IsIGdyb3VwPXRvcGljbywgY29sb3I9dG9waWNvLCBmaWxsPXRvcGljbykpKwogIGdlb21fbGluZSgpKwogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzY2FsZXM6OnByZXR0eV9icmVha3MoMTApKSsKICAjIGRpcmVjdGxhYmVsczo6Z2VvbV9kbChhZXMobGFiZWwgPSB0b3BpY28pLCBtZXRob2Q9bGlzdCgidG9wLnFwIiwgY2V4ID0gLjc1KSkrCiAgdGhlbWVfbWluaW1hbCgpCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gJ25vbmUnKQoKcGxvdGx5OjpnZ3Bsb3RseShwbG90KQpgYGAKCm9iczogRWwgZGF0YXNldCB0aWVuZSBtdWNobyBkZSBBbGxlbmRlLCBUw7NwaWNvIDYuIFRhbCB2ZXogaGFicmlhIHF1ZSBzdWJzYW1wbGVhci4KCmBgYHtyfQpUZXJtcwpgYGAKCgojIyBncmFmbyBkZSBhdXRvcmVzCgoKYGBge3J9CmRpc3RfdG9waWNvc19hdXRvciA8LSBkaXN0X3RvcGljb3MgJT4lIAogIGdyb3VwX2J5KGF1dG9yKSAlPiUgCiAgc3VtbWFyaXNlX2FsbCh+bWVhbigueCwgbmEucm0gPSBUKSkgJT4lIAogIG11dGF0ZShmZWNoYT1yb3VuZChmZWNoYSkpCgphZGpNYXQgPSBjb3NpbmUodChhcy5tYXRyaXgoZGlzdF90b3BpY29zX2F1dG9yWywzOjIxXSkpKSAKCmNvbG5hbWVzKGFkak1hdCkgPC0gZGlzdF90b3BpY29zX2F1dG9yJGF1dG9yCnJvd25hbWVzKGFkak1hdCkgPC0gZGlzdF90b3BpY29zX2F1dG9yJGF1dG9yCgphZGpNYXRbMTo1LDE6NV0KCmZpdmVudW0oYWRqTWF0KQoKYGBgCgpgYGB7cn0KI2xhIHBhc28gYSBkaWNvdG9taWNhLCBubyBxdWllcm8gcXVlIG1lIHF1ZWRlIG11eSBkZW5zYSwgYXNpIHF1ZSBwb25nbyBjb21vIHB1bnRvIGRlIGNvcnRlIHVuIHZhbG9yIGFsdG8KCmFkak1hdFthZGpNYXQ+MC45XSA8LSAxCgphZGpNYXRbYWRqTWF0PDAuOV0gPC0gMAoKYWRqTWF0WzE6NSwxOjVdCgpgYGAKCmBgYHtyfQpnID0gZ3JhcGhfZnJvbV9hZGphY2VuY3lfbWF0cml4KGFkak1hdCwgd2VpZ2h0ZWQ9IE5VTEwsIG1vZGU9InVuZGlyZWN0ZWQiLCBkaWFnPUZBTFNFKQoKVihnKSRmZWNoYSA8LSBkaXN0X3RvcGljb3NfYXV0b3IkZmVjaGEgI2FncmVnbyBsYSBmZWNoYSBjb21vIGF0cmlidXRvIGRlIGNhZGEgYXV0b3IgCmBgYAoKYGBge3J9Cm1lYW4oZGVncmVlKGcpKQpgYGAKCmBgYHtyfQoKCmwgPC0gbGF5b3V0X25pY2VseShnKQoKcGxvdChnLGVkZ2UuYXJyb3cuc2l6ZT0uMiwgdmVydGV4LnNpemU9NCx2ZXJ0ZXguZnJhbWUuY29sb3I9IiNmZmZmZmYiLAogICAgICAgICAgICAgdmVydGV4LmxhYmVsPSIiLCB2ZXJ0ZXgubGFiZWwuY29sb3I9ImJsYWNrIiwgCiAgICAgICAgICAgICBsYXlvdXQ9bCkKCmBgYAoKYGBge3IgIGZpZy5oZWlnaHQ9MTAsIGZpZy53aWR0aD0xMH0KYXNfdGJsX2dyYXBoKGcpICU+JSAKICBmaWx0ZXIoZmVjaGE+MTgwMCkgJT4lIAogIGFjdGl2YXRlKG5vZGVzKSAlPiUKICBtdXRhdGUoaW1wb3J0YW5jZSA9IGNlbnRyYWxpdHlfZGVncmVlKCkpICU+JSAKICBmaWx0ZXIoaW1wb3J0YW5jZSA+MSkgJT4lIAojIGFzX3RibF9ncmFwaChnKSAlPiUgCiMgICBmaWx0ZXIoZmVjaGE+MTgwMCwKIyAgICAgICAgICAhZGVncmVlKGcpPDIpICU+JSAKZ2dyYXBoKCkgKyAKICAgIGdlb21fZWRnZV9saW5rKGNvbG9yPSdncmV5JykgKyAKICAgIGdlb21fbm9kZV9wb2ludChhZXMoY29sb3I9ZmVjaGEpKSsKICAgIGdlb21fbm9kZV90ZXh0KGFlcyhsYWJlbD1uYW1lKSxjaGVja19vdmVybGFwID0gVCxudWRnZV95ID0tLjUgKSArCiAgdGhlbWVfdm9pZCgpKwogIHNjYWxlX2NvbG9yX2dyYWRpZW50Mihsb3cgPSAnZGFya29yYW5nZTMnLG1pZCA9J2RhcmtncmVlbicgLCBoaWdoID0gJ2RvZGdlcmJsdWUnLCBtaWRwb2ludCA9IDE5MDApCmBgYAoKCgoKYGBge3J9CmFzX3RibF9ncmFwaChnKSAlPiUgCiAgZmlsdGVyKGZlY2hhPjE4MDApICU+JSAKICBhY3RpdmF0ZShub2RlcykgJT4lCiMgYXNfdGJsX2dyYXBoKGcpICU+JSAKIyAgIGZpbHRlcihmZWNoYT4xODAwLAojICAgICAgICAgICFkZWdyZWUoZyk8MikgJT4lIApnZ3JhcGgoKSArIAogICAgZ2VvbV9lZGdlX2xpbmsoY29sb3I9J2dyZXknKSArIAogICAgZ2VvbV9ub2RlX3BvaW50KGFlcyhjb2xvcj1mZWNoYSkpKwogICAgZ2VvbV9ub2RlX3RleHQoYWVzKGxhYmVsPW5hbWUpLGNoZWNrX292ZXJsYXAgPSBULG51ZGdlX3kgPS0uNSApICsKICB0aGVtZV92b2lkKCkrCiAgc2NhbGVfY29sb3JfZ3JhZGllbnQyKGxvdyA9ICdkYXJrb3JhbmdlMycsbWlkID0nZGFya2dyZWVuJyAsIGhpZ2ggPSAnZG9kZ2VyYmx1ZScsIG1pZHBvaW50ID0gMTkwMCkKYGBgCgoKYGBge3J9CmdyYWZvX3Bsb3QgPC0gYXNfdGJsX2dyYXBoKGcpICU+JSAKICBmaWx0ZXIoZmVjaGE+MTgwMCkgJT4lIAogIGFjdGl2YXRlKG5vZGVzKSAlPiUKIyBhc190YmxfZ3JhcGgoZykgJT4lIAojICAgZmlsdGVyKGZlY2hhPjE4MDAsCiMgICAgICAgICAgIWRlZ3JlZShnKTwyKSAlPiUgCmdncmFwaCgpICsgCiAgICBnZW9tX2VkZ2VfbGluayhjb2xvcj0nZ3JleScpICsgCiAgICBnZW9tX25vZGVfcG9pbnQoYWVzKGNvbG9yPWZlY2hhLGxhYmVsPW5hbWUpKSsKICAgIGdlb21fbm9kZV90ZXh0KGFlcyhsYWJlbD1uYW1lKSxjaGVja19vdmVybGFwID0gVCxudWRnZV95ID0tLjUgKSArCiAgdGhlbWVfdm9pZCgpKwogIHNjYWxlX2NvbG9yX2dyYWRpZW50Mihsb3cgPSAnZGFya29yYW5nZTMnLG1pZCA9J2RhcmtncmVlbicgLCBoaWdoID0gJ2RvZGdlcmJsdWUnLCBtaWRwb2ludCA9IDE5MDApCgoKcGxvdGx5OjpnZ3Bsb3RseShncmFmb19wbG90KQpgYGAKCg==